from .geo import Dot, Line
import math
import turtle

def _next_dot(dot:Dot, angle:float, length:float, color:str='red', size=1):
    '''
    获取下一个点
    '''

    angle_rad = angle * math.pi / 180
    dx = length * math.cos(angle_rad)
    dy = length * math.sin(angle_rad)
    x = dot.x + dx
    y = dot.y + dy
    return Dot(x,y, color, size)

# 要素类
class AccoElem:
    '''
    财务会计要素类
    '''
    DEFAULT_COLOR = 'green'
    DEFAULT_THICKNESS = 8
    DEFAULT_FONT_NAME = 'heiti'
    DEFAULT_FONT_SIZE = 12
    DEFAULT_FONT_COLOR = 'black'
    DEFAULT_OFFSET = 15.
    DEFAULT_ANGLE = 0.
    DEFAULT_ALIGN = 'center'

    def __init__(self, name:str, value:dict, scale:float,
                 start_dot:Dot=Dot(x=0,y=0), dot_color:str='red', dot_size:int=16,
                 color:str|list='green', thickness:int|list=8, angle:float|list=0., 
                 font_name='heiti', font_size:int|list=12, font_color:str|list='black', 
                 offset:float|list=15., align:str='center', speed=5):
        '''
        初始化
        '''
        self.name = name
        self.value = value
        self._scale = scale
        self.start_dot = start_dot
        self.dot_color = dot_color
        self.dot_size = dot_size
        self._color = color
        self._thickness = thickness
        self._angle = angle
        self._font_name = font_name
        self._font_size = font_size
        self._font_color = font_color
        self._offset = offset
        self._align = align
        self._speed = speed

    def get_categories(self):
        '''
        获取类别
        return list
        '''
        return [i for i in self.value.keys()]
    def get_amounts(self):
        '''
        获取金额
        return list
        '''
        result = {}
        for k,v in self.value.items():
            result[k] = v.get('金额',0)
        return result
    def get_areas(self):
        '''
        获取面积
        return list
        '''
        result = {}
        for k,v in self.value.items():
            result[k] = v.get('面积',0)
        return result
    def get_prices(self):
        '''
        获取单价
        return list
        '''
        result = {}
        for k,v in self.value.items():
            result[k] = v.get('单价',0)
        return result
    def get_colors(self):
        '''
        获取线段的颜色
        return list
        '''
        if isinstance(self._color, str):
            return [self._color for i in self.value.keys()]
        elif isinstance(self._color, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._color)
            if diff > 0:
                self._color += [self.DEFAULT_COLOR for i in range(diff)]
            elif diff < 0:
                self._color = self._color[:cate_len]

            return self._color    
    def get_thickness(self):
        '''
        获取粗细
        return list
        '''
        if isinstance(self._thickness, int):
            return [self._thickness for i in self.value.keys()]
        elif isinstance(self._thickness, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._thickness)
            if diff > 0:
                self._thickness += [self.DEFAULT_THICKNESS for i in range(diff)]
            elif diff < 0:
                self._thickness = self._thickness[:cate_len]

            return self._thickness    
    def get_angle(self):
        '''
        获取角度
        return list
        '''
        if isinstance(self._angle, float):
            return [self._angle for i in self.value.keys()]
        elif isinstance(self._angle, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._angle)
            if diff > 0:
                self._angle += [self.DEFAULT_ANGLE for i in range(diff)]
            elif diff < 0:
                self._angle = self._angle[:cate_len]

            return self._angle
    def get_length(self)->float:
        '''
        获取长度
        return list
        '''
        amounts = self.get_amounts()
        return [round(amount* self._scale, 0) for _, amount in amounts.items()]    
    def get_endpoints(self):
        '''
        获取第一个点
        return Dot
        '''
        result = []
        first_dot = self.start_dot
        first_dot.color = self.dot_color
        first_dot.size = self.dot_size
        angles = self.get_angle()
        lengths = self.get_length()
        item_len = len(self.value.keys())
        for i in range(item_len):
            angle = angles[i]
            length = lengths[i]
            second_dot = _next_dot(first_dot, angle, length, self.dot_color, self.dot_size)
            result.append({'first_dot': first_dot, 'second_dot': second_dot})
            first_dot = second_dot
        return result
        
    def get_fontname(self):
        '''
        获取字体名称
        return list
        '''
        if isinstance(self._font_name, str):
            return [self._font_name for i in self.value.keys()]
        elif isinstance(self._font_name, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._font_name)
            if diff > 0:
                self._font_name += [self.DEFAULT_FONT_NAME for i in range(diff)]
            elif diff < 0:
                self._font_name = self._font_name[:cate_len]

            return self._color    
    def get_fontsize(self):
        '''
        获取字体大小
        return list
        '''
        if isinstance(self._font_size, int):
            return [self._font_size for i in self.value.keys()]
        elif isinstance(self._font_size, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._font_size)
            if diff > 0:
                self._font_size += [self.DEFAULT_FONT_SIZE for i in range(diff)]
            elif diff < 0:
                self._font_size = self._font_size[:cate_len]

            return self._font_size
    def get_font_color(self):
        '''
        获取字体颜色
        return list
        '''
        if isinstance(self._font_color, str):
            return [self._font_color for i in self.value.keys()]
        elif isinstance(self._font_color, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._font_color)
            if diff > 0:
                self._font_color += [self.DEFAULT_FONT_COLOR for i in range(diff)]
            elif diff < 0:
                self._font_color = self._font_color[:cate_len]

            return self._font_color
    def get_offset(self):
        '''
        获取标注相对线段的偏移量
        return list
        '''
        if isinstance(self._offset, float):
            return [self._offset for i in self.value.keys()]
        elif isinstance(self._offset, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._offset)
            if diff > 0:
                self._offset += [self.DEFAULT_OFFSET for i in range(diff)]
            elif diff < 0:
                self._offset = self._offset[:cate_len]

            return self._offset
    def get_align(self):
        '''
        获取标注相对线段的对齐方式
        return list
        '''
        if isinstance(self._align, str):
            return [self._align for i in self.value.keys()]
        elif isinstance(self._align, list):
            cate_len = len(self.get_categories())
            diff = cate_len - len(self._align)
            if diff > 0:
                self._align += [self.DEFAULT_ALIGN for i in range(diff)]
            elif diff < 0:
                self._align = self._align[:cate_len]

            return self._align      
    def get_total_amount(self):
        '''
        获取总金额
        return int
        '''
        return sum(self.get_amounts().values())    
    
    def split(self):
        '''
        拆分数据，每项对应一根线段所需属性集
        return dict
        '''
        name = self.name
        total_amount = self.get_total_amount()    
        categories = self.get_categories()
        amounts = self.get_amounts()
        lengths = self.get_length()
        areas = self.get_areas()
        prices = self.get_prices()
        angles = self.get_angle()
        colors = self.get_colors()
        thickness = self.get_thickness()
        endponts = self.get_endpoints()
        fontnames = self.get_fontname()
        fontsizes = self.get_fontsize()
        fontcolors = self.get_font_color()
        offsets = self.get_offset()
        aligns = self.get_align()
        line_segments_attrs = {
            'name' : name,
            'total_amount' : total_amount,
            'scale' : self._scale,
            'start_dot' : self.start_dot,
        }
        for i, category in enumerate(categories):
            line_segments_attrs[category] = {
                'content' : category + ':' + str(amounts[category]) + '万元',
                'value' : amounts[category],
                'length' : lengths[i],
                'angle' : angles[i],
                'color' : colors[i],
                'thinkness' : thickness[i],               
                'endpoints' : endponts[i],
                'font_name' : fontnames[i],
                'font_size' : fontsizes[i],
                'font_color' : fontcolors[i],
                'offset' : offsets[i],
                'align' : aligns[i],
            }

        return line_segments_attrs

    def draw(self):
        '''
        绘制条形图
        '''
        line_segments_attrs = self.split()
        categories = self.get_categories()
        for category in categories:
            line_segment = line_segments_attrs[category]
            first_dot = line_segment['endpoints']['first_dot']
            second_dot = line_segment['endpoints']['second_dot']
            angle = line_segment['angle']
            color = line_segment['color']
            thickness = line_segment['thinkness']
            font_name = line_segment['font_name']
            font_size = line_segment['font_size']
            font_color = line_segment['font_color']
            content = f'{line_segment["content"]}'
            offset = line_segment['offset']

            line = Line(first_dot=first_dot, second_dot=second_dot, angle=angle, 
                        color=color, thickness=thickness,
                        font_name=font_name, font_size=font_size, font_color=font_color,
                        content=content, offset=offset, speed=self._speed)
            line.draw()
            
            line.annotate()
    def get_all_x(self):
        '''
        绘制经过每个点的垂直线
        '''
        all_x = []
        endpoints = self.get_endpoints()
        for i,endpoint in enumerate(endpoints):
            if i==0:
                all_x.append(endpoint['first_dot'].x)
                all_x.append(endpoint['second_dot'].x)
            else:
                all_x.append(endpoint['second_dot'].x)
        return all_x
    def get_last_dot(self):
        '''
        获取最后一个点
        '''
        endpoints = self.get_endpoints()
        return endpoints[-1]['second_dot']

class AccoElems:
    '''
    财务要素集
    '''

    def __init__(self, elements:list):
        '''
        elements: AccoElem 对象集
        '''
        self.elements = elements

    def get_endpoints(self):
        '''
        获取所有要素的终点
        '''
        endpoints = []
        for element in self.elements:
            endpoints += element.get_endpoints()           
        return endpoints

    def get_all_x(self):
        '''
        获取所有要素的x坐标
        '''
        all_x = []
        for element in self.elements:
            all_x += element.get_all_x()
        return sorted(list(set(all_x)))

    def get_last_dots(self):
        '''
        得到所有要素最后一个点的集合
        '''
        last_dots = []
        for element in self.elements:
            last_dots.append(element.get_last_dot())
        return last_dots
    
    def get_total_amounts(self):
        '''
        获取各要素总金额集
        '''
        total_amounts = []
        for element in self.elements:
            total_amounts.append(element.get_total_amount())
        return total_amounts
    
    def draw(self):
        '''
        绘制差异
        '''  
        last_dots = self.get_last_dots()
        x = [dot.x for dot in last_dots]
        y = [dot.y for dot in last_dots]
        total_amounts = self.get_total_amounts()
        diff = abs(total_amounts[0] - total_amounts[1])
        percent = diff / max(total_amounts) * 100
        content = f'差异：{diff}万元 {percent:.2f}%'
        distance = abs(y[0] - y[1])
        top_y = max(y) + distance
        first_dot = Dot(x=x[1], y=top_y)  
        second_dot = Dot(x=x[0], y=top_y)
        angle = 0
        line = Line(first_dot=first_dot, second_dot=second_dot, angle=angle, color='red', thickness=9, 
                    font_size=16,
                    content=content, offset=15)
        line.draw()
        line.annotate()
            
    # 绘制节点垂直网格线
    def draw_vert_grid_line(self, bottom_y=0, top_y=10, color='black', thickness=1, line_type='solid'):
        '''
        目标：绘制节点垂直网格线
        要求：
            1. 节点可调
            2. 颜色可调
            3. 粗细可调
            5. 线型可调
        '''
        all_x = self.get_all_x()
        for x in all_x:    
            first_dot = Dot(x=x, y=bottom_y)
            second_dot = Dot(x=x, y=top_y)
            line = Line(first_dot=first_dot, second_dot=second_dot, color=color, thickness=thickness, line_type=line_type)
            line.draw()
        
